﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Specifies the layout of an element depending on it's parent element.
    /// </summary>
    public class Margin : ICloneable
    {
        #region ctors

        public Margin()
        {
            left = right = top = bottom = NaN;
        }

        public Margin(int size)
        {
            left = right = top = bottom = size;
        }

        public Margin(int? left, int? top, int? right, int? bottom)
        {
            this.left = left ?? NaN;
            this.top = top ?? NaN;
            this.right = right ?? NaN;
            this.bottom = bottom ?? NaN;
        }

        public Margin(Rectangle rect)
        {
            left = rect.Left;
            top = rect.Top;
            right = rect.Right;
            bottom = rect.Bottom;
        }


        public Margin(bool horizontalCentered, bool verticalCentered)
            : base()
        {
            HorizontalCentered = horizontalCentered;
            VerticalCentered = verticalCentered;
        }

        public override string ToString()
        {
            return string.Format("left: {0}, top: {1}, right: {2}, bottom: {3}", LeftZero, TopZero, RightZero, BottomZero);
        }

        #endregion
        #region fields

        const int centerValue = int.MaxValue;
        const int NaN = int.MinValue;

        private int left, right, top, bottom;

        #endregion
        #region Properties

        public int? Left { get { return left != NaN ? (int?)left : null; } set { left = value ?? NaN; } }
        public int? Right { get { return right != NaN ? (int?)right : null; } set { right = value ?? NaN; } }
        public int? Top { get { return top != NaN ? (int?)top : null; } set { top = value ?? NaN; } }
        public int? Bottom { get { return bottom != NaN ? (int?)bottom : null; } set { bottom = value ?? NaN; } }

        public bool IsLeftEmpty { get { return left == NaN; } }
        public bool IsRightEmpty { get { return right == NaN; } }
        public bool IsTopEmpty { get { return top == NaN; } }
        public bool IsBottomEmpty { get { return bottom == NaN; } }

        public int TopZero { get { return top != NaN ? top : 0; } }
        public int LeftZero { get { return left != NaN ? left : 0; } }
        public int RightZero { get { return right != NaN ? right : 0; } }
        public int BottomZero { get { return bottom != NaN ? bottom : 0; } }
        public int Width { get { return LeftZero + RightZero; } }
        public int Height { get { return TopZero + BottomZero; } }

        /// <summary>
        /// Gets or sets whether to layout horizontally centered inside the parent element.
        /// </summary>
        public bool HorizontalCentered
        {
            get { return left == centerValue; }
            set
            {
                if (value)
                {
                    right = left = centerValue;
                }
                else
                {
                    left = NaN;
                    right = NaN;
                }
            }
        }

        /// <summary>
        /// Gets or sets whether to layout vertically centered inside the parent element.
        /// </summary>
        public bool VerticalCentered
        {
            get { return top == centerValue; }
            set
            {
                if (value)
                {
                    bottom = top = centerValue;
                }
                else
                {
                    top = NaN;
                    bottom = NaN;
                }
            }
        }


        public bool IsEmpty
        {
            get
            {
                return left == NaN && right == NaN && top == NaN && bottom == NaN;
            }
        }

        public static readonly Margin Empty = new Margin();
        public static readonly Margin Zero = new Margin(0);
        public static readonly Margin Centered = new Margin(true, true);

        #endregion
        #region Methods

        public override int GetHashCode()
        {
            int a = left;
            int b = right;
            int c = top;
            int d = bottom;
            return (((a ^ ((c << 13) | (c >> 0x13))) ^ ((b << 0x1a) | (b >> 6))) ^ ((d << 7) | (d >> 0x19)));

        }

        public override bool Equals(object obj)
        {
            if (!(obj is Margin)) return false;

            Margin m = (Margin)obj;
            return m.left == left && m.right == right && m.top == top && m.bottom == bottom;
        }

        public virtual bool Equals(Margin m)
        {
            if (m == null) return false;
            return m.left == left && m.right == right && m.top == top && m.bottom == bottom;
        }



        public static bool IsNullOrEmpty(Margin margin)
        {
            Object o = (Object)margin;
            if (o == null) return true;
            return margin.IsEmpty;
        }

        /// <summary>
        /// Layouts a given rectangle depending on it's container size.
        /// </summary>
        /// <param name="rect">The rectangle to layout.</param>
        /// <param name="containerSize">Size of the container that contains the element for which this rectangle is calculated.</param>
        /// <returns>Calculated Rectangle for the element.</returns>
        public virtual Rectangle Layout(Rectangle rect, Size containerSize)
        {
            if (HorizontalCentered)
            {
                rect.X = (containerSize.Width - rect.Width) >> 1;
            }
            else
            {
                if (left != NaN && right != NaN)
                {
                    rect.X = left;
                    rect.Width = containerSize.Width - (right + left);
                }
                else
                {
                    if (left != NaN) rect.X = left;
                    if (right != NaN) rect.X = containerSize.Width - rect.Width - right;
                }
            }

            if (VerticalCentered)
            {
                rect.Y = (containerSize.Height - rect.Height) >> 1;
            }
            else
            {
                if (top != NaN && bottom != NaN)
                {
                    rect.Y = top;
                    rect.Height = containerSize.Height - (bottom + top);
                }
                else
                {
                    if (top != NaN) rect.Y = top;
                    if (bottom != NaN) rect.Y = containerSize.Height - rect.Height - bottom;
                }
            }
            rect.Width = Math.Max(0, rect.Width);
            rect.Height = Math.Max(0, rect.Height);
            return rect;
        }

        public Rectangle LayoutChild(Rectangle bounds)
        {
            Point p = bounds.Location;
            bounds.Location = Point.Empty;
            Size size = new Size(bounds.Width, bounds.Height);
            Rectangle r = Layout(bounds, size);
            r.Offset(p.X, p.Y);
            return r;
        }



        #endregion
        #region operators

        //public static bool operator ==(Margin a, Margin b)
        //{
        //    if (a == null && b == null) return true;
        //    if (a == null || b == null) return false;
        //    return a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom;
        //}


        //public static bool operator !=(Margin a, Margin b)
        //{
        //    if (a == null && b == null) return false;
        //    if (a == null || b == null) return true;
        //    return a.left != b.left || a.right != b.right || a.top != b.top || a.bottom != b.bottom;
        //}


        public static implicit operator Margin(int size)
        {
            return new Margin(size);
        }



        #endregion

        #region ICloneable Members

        object ICloneable.Clone()
        {
            return this.Clone();
        }

        public virtual Margin Clone()
        {
            return new Margin(left, top, right, bottom);
        }

        #endregion

    }
}
